#ifndef __BUOLA_GUI_X11_CX11WINDOW_H__
#define __BUOLA_GUI_X11_CX11WINDOW_H__

#include <buola/gui/x11/auto.h>
#include <buola/gui/cphysicalwindow.h>
#include <X11/Xlib.h>

#include <unordered_map>

namespace buola { namespace gui { namespace x11 {

class CX11WindowWM;

///////////////////////////////////////////////////////////////////////////
////////////////////////                         //////////////////////////
////////////////////////      Some structs        /////////////////////////
/////////////////////////                       ///////////////////////////
///////////////////////////////////////////////////////////////////////////

struct SXEmbedInfo
{
    uint32_t mVersion;
    uint32_t mFlags;
};

///////////////////////////////////////////////////////////////////////////
////////////////////////                         //////////////////////////
////////////////////////    Class CX11Window      /////////////////////////
/////////////////////////                       ///////////////////////////
///////////////////////////////////////////////////////////////////////////

class CX11Window : public CPhysicalWindow
{
public:
    enum EFlags
    {
        FLAG_NONE=0,
        FLAG_RESIZEPENDING=(1<<0),  //!<size change requested, waiting event
        FLAG_MOVEPENDING=(1<<1),    //!<pos change requested, waiting event
        FLAG_SHAPED=(1<<4),         //!<has a shape defined
        FLAG_EMBEDDED=(1<<5),       //!<embedded using XEmbed
        FLAG_EMBEDDER=(1<<6),       //!<embedder using XEmbed
        FLAG_OVERRIDE=(1<<7),       //!<override redirect
        FLAG_ROOT=(1<<8),           //!<is the root window
        FLAG_DONTFOCUS=(1<<9),      //!<shouldn't get the input focus
        FLAG_FOREIGN=(1<<10)        //!<we didn't create the window
    };

    DECLARE_FRIEND_ENUM_BITOPS(EFlags)

////////////////////////////////
// constructor and destructor
//
    CX11Window(CWindow *pWindow,CX11Display *pDisplay);
    CX11Window(Window,CX11Display*);
    virtual ~CX11Window();

public:
////////////////////////////////////////////////////////////////
////////////////////////// interface ///////////////////////////
////////////////////////////////////////////////////////////////
    void AddFilter(CX11EventFilter*);
    void RemoveFilter(CX11EventFilter*);

/////////////////////
// window creation
//
public:
//from a window ID
    void Create(CPhysicalWindow*,const CPoint_d&,const CSize_d&);
    void Destroy();

//direct protocol access
    void ClearArea(const CRect_i &pRect,bool);
    void Map(bool);
    void Unmap();
    void Reparent(CPhysicalWindow*,const CPoint_d&);
    void AddToSaveSet();
    void RemoveFromSaveSet();
    void Lower();
    void Raise(const std::vector<CPhysicalWindow*> &pExc);
    void StayOnTop(CPhysicalWindow *pOther);
    void SelectInput(EEvents pEvents);
    void SetGravity(int);
    void SetBackingStore(bool);
    void SetNoShape(int=SHAPE_BOUND);

    void SetShape(const CRegion &pRegion,int=SHAPE_BOUND,
                                const CPoint_d& =CPoint_d(nZero));
    void SetShapeFromMask(CSurface*,int,const CPoint_d&);
    void SetShapeFromRect(const CRect_d&,int,const CPoint_d&);
    void SetShapeFromWindow(CPhysicalWindow*,int,const CPoint_d&);
    void SetShapeFromFixesRegion(const CFixesRegion &pRegion,int=SHAPE_BOUND,
                                const CPoint_d& =CPoint_d(nZero));

    void SetFocus();
    void UpdateState();

    void SetPointer(PPointer);

//XEmbed client
    void SendXEmbedMessage(int pOpcode,int pDetail=0,int pData1=0,
                            int pData2=0);
    void Embed(CPhysicalWindow *pChild);
    void Embed(TID pID);
    void SetEmbedInfo();

//button grabbing
    void GrabButton(EMouseButton pButton,EKeyMod pMods,
                    bool pConfine,CPointer *pCursor);
    void UngrabButton(EMouseButton pButton,EKeyMod pMods);

//physical movement
    void Move(const CPoint_d&);
    void Resize(const CSize_d&);
    void MoveResize(const CPoint_d&,const CSize_d&);

    void DispatchAll();

    CPoint_d TranslateCoords(const CPoint_d &pPoint,CPhysicalWindow *pWindow);

//window-managed
    void Freeze(bool pFrozen);
    EWindowState GetState();
    void Iconify(bool);
    void Maximize(EWindowState);
    void Shade(bool);
    void Sticky(bool);
    void SetDesktop(int);
    void FullScreen(bool);
    void StartMoveOperation(CMouseEvent *pE);
    void StartResizeOperation(CMouseEvent *pE,EDragState pType);
    void SetStrut(const CRect_d &pRect);
    void SetOpacity(double);
    void SetTitle(const std::wstring &pTitle);
    void SetIcon(PIcon pIcon);
    void UpdateHints();

    void OnXEmbedRequestFocus();

    CExposeHelper* GetExposeHelper();

////////////////////////////////
// inter-client communication
//
public:
    void SendSyntheticExposeEvent(const CRect_d &pRect);
    void SendSyncRequest();

    void SendClientMessage(CX11Window*,TID,int=0,int=0,int=0,int=0,int=0,
                long pMask=-1);
    void SendClientMessage(TID,int=0,int=0,int=0,int=0,int=0,
                long pMask=-1);

//////////////////////
// some information
//
    bool IsRoot()           {   return GetFlag(FLAG_ROOT);     }
    bool IsForeign()        {   return GetFlag(FLAG_FOREIGN);      }
    bool IsWindowManaged()  {   return (!GetFlag(FLAG_FOREIGN)&&IsTopLevel()&&!GetFlag(FLAG_OVERRIDE));  }
    bool IsManaged()        {   return ((!GetFlag(FLAG_OVERRIDE))&&(!mParent||mParent->IsForeign()));  }
    bool IsTopLevel()       {   return (mParent&&mParent->IsRoot());    }

    virtual void OnSizeHintsChanged();
    virtual void OnActivateForeign(bool pActivated);

    CX11Display *GetDisplay()   {   return mDisplay;    }
    Window GetX11ID()           {   return mX11ID;      }

    Visual *XVisual()           {   return mVisual;     }
    int Depth()                 {   return mDepth;      }
    
//////////////////////////////////////////////////
//////////////// static interface ////////////////
//////////////////////////////////////////////////

/////////////////////
// finding windows
//
    static CX11Window *GetX11Window(Window,bool=false);

protected:
////////////////////////////////////////////////////
//////////////// internal interface ////////////////
////////////////////////////////////////////////////
    static int GetXShapeOp(int pOp);
    long GetXEvents(EEvents pE);
    unsigned long GetXShapeEvents(EEvents pE);

public:
///////////////////////
// window properties
//
    void SetWMInfo();

    bool GetWMProperty(Atom,std::wstring&,Atom=0);
    bool GetWMProperty(Atom,int*);
    bool SetWMProperty(Atom,const std::wstring&,Atom=0);
    bool SetWMProperty(Atom,const std::wstring&,const std::wstring&,Atom=0);
    bool SetWMProperty(Atom,const std::string&,Atom=0);
    bool SetWMProperty(Atom,const std::string&,const std::string&,Atom=0);
    bool SetWMProperty(Atom,Atom*,int);
    bool SetWMProperty(Atom,int*,int);
    bool SetWMProperty(Atom,int);
    bool SetWMPropertyAsAtom(Atom,Atom);
    void RemoveWMProperty(Atom);

    bool AcquireSelection(Atom);
    void ReleaseSelection(Atom);

    void SetDesiredVisual(Visual *pVisual,int pDepth);

    CGLXInterface *GLXInterface();
    
    static bool ShouldManage(CWindow *pWindow);
    
protected:
////////////////////////////////////////
//////////// From CDrawable ////////////
////////////////////////////////////////
public:

    img::CGC *GetGC(img::EGCCaps pCaps);
//GCs
    CSize_i SurfaceSize();

public:

    virtual void BitBlt(const CPoint_d&,const CSize_d&,CSurface*,const CPoint_d&,bool);

private:
//! \name physical window flags
//@{
    void SetFlags(EFlags pFlags)    {   mFlags=pFlags;    }
    EFlags GetFlags()               {   return mFlags;    }

    void SetFlag(EFlags pFlag)      {   mFlags|=pFlag;    }
    void UnsetFlag(EFlags pFlag)    {   mFlags&=(~pFlag);   }
    bool GetFlag(EFlags pFlag)      {   return ((mFlags&pFlag)==pFlag); }

//@}

private:
////////////////////////////////////////
//////////// internal data /////////////
////////////////////////////////////////
    Window mX11ID;
    CX11Display *mDisplay;

    PPointer mCursor;

    bool mInputOnly;
    Visual *mVisual;
    int mDepth;
    
    std::vector<CX11EventFilter*> mFilters;

    CX11WindowWM *mWM;

    CX11Window *mParent;
    XWindowAttributes mXAttrs;

    long mInitialEventMask; //used when we had already selected events
                            //on some foreign window

    static std::unordered_map<TID,CX11Window*> mAllX11Windows;

    CX11Window *mXEmbeddee;

    EFlags mFlags;

    std::unique_ptr<CExposeHelper> mExposeHelper;
    std::unique_ptr<CGLXInterface> mGLXInterface;
    
////////////////////////////////////////
////////////////////////////////////////
/////////////// Friends ////////////////
////////////////////////////////////////
////////////////////////////////////////
    friend class CX11Dispatcher;
    friend class CX11DndFilter;
    friend class CX11GC;
    friend class CX11WindowWM;
};

/*namespace x11*/ } /*namespace gui*/ } /*namespace buola*/ }

#endif
